using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using API;
using LitJson;
using Newtonsoft.Json.Linq;
using Xamarin.Essentials;

namespace mb
{
    public class ServerLoader
    {
        static IStartOption startOption = ServiceLocator.Instance.Get<IStartOption>();
        static ILog logger = App.logger;

        //-////////////////////////////////////////////////////
        static int ConfigVersion20190502 = 20190502;
        static int ConfigVersion20190514 = 20190514;
        static int ConfigVersionNow = ConfigVersion20190514;

        //必须把key加上版本号，以防止新版本有全新初始数据时，被之前的老数据拦住
        static string SSKeys = "server" + ConfigVersionNow;
        static string SSFile = Xamarin.Essentials.FileSystem.CacheDirectory + "/" + SSKeys;

        //-////////////////////////////////////////////////////
        public enum LoadResult
        {
            STEP_NOTIFY,
            NOT_AVAILABLE,
            NEED_UPDATE,
            OK,
        }

        public enum ServerType
        {
            Update,
            Share,
            Pay,
            User,
            Video,
            Count,
        }

        public class ServerItem
        {
            public string site;
            public long availableTime;
            public long lastConnectTime;
            public bool delete;
        }
        public class PkgItem
        {
            public string limit;
            public string url;
        }
        public class ServerConfig
        {
            public int version;
            public Dictionary<string, PkgItem> pkgs;
            public List<ServerItem>[] servers;
            public ServerConfig()
            {
                servers = new List<ServerItem>[(int) ServerType.Count];
                for (int i = 0; i < servers.Length; i++)
                {
                    servers[i] = new List<ServerItem>();
                }
            }
        }
        ServerConfig serverConfig;

        public ServerLoader() { }

        public async void StartLoad(Action<LoadResult, string> handler = null)
        {
            await System.Threading.Tasks.Task.Delay(1000);
            await LoadInit();
            if (serverConfig == null)
            {
                handler?.Invoke(LoadResult.NOT_AVAILABLE, "解析配置出错，安装包可能已损坏，请重新下载安装");
                return;
            }
            //先加载本地ip列表，这个列表最初从安装包预置文件里获取
            var servers = GetAvailabeServers(ServerType.Update);

            //请求参数
            var id = Xamarin.Essentials.Preferences.Get("UserId", "");

            logger.Log("------------------->id:" + id);
            string device = Xamarin.Essentials.DeviceInfo.Name;
            string body = LitJson.JsonMapper.ToJson(new { id = id, device = device });

            string selectUrl = null;
            ServerConfig onlineConfig = null;

            logger.Log("========try connect server=========");
            //逐个尝试列表中的ip，一旦连上就获取新ip存起来

            foreach (var si in ForEachHelper.WithIndex(servers))
            {
                var site = si.Value.site;
                // var myTestServer = startOption.GetStringValue("getMyTestServer");
                // if (myTestServer != null)
                // {
                //     site = myTestServer;
                // }
                logger.Log("check server", site);
                handler(LoadResult.STEP_NOTIFY, $"连接检查[{si.Index+1}/{servers.Count}]");
                (int code, string msg) res = await mb.Http.MakeRequest(new Http.Option
                {
                    Url = $"http://{site}/check",
                        Method = HttpMethod.Post,
                        Body = body,
                        TimeoutSec = 5,

                });
                if (res.code != 200)
                {
                    logger.Log("check failed", res.code);
                    continue;
                }

                onlineConfig = LitJson.JsonMapper.ToObject<ServerConfig>(res.msg);
                selectUrl = site;
                break;
            }

            if (onlineConfig == null)
            {
                handler?.Invoke(LoadResult.NOT_AVAILABLE, "服务器维护中，请明天再试");
                return;
            }

            try
            {
                logger.Log($"check ok {selectUrl}\n");

                //保存新获得到的服务器
                Merge(onlineConfig, selectUrl);
                Save();

                //检查大版本是否要更新
                var platform = Xamarin.Essentials.DeviceInfo.Platform.ToString();
                var pkg = onlineConfig.pkgs[platform];
                var limitVersion = Version.Parse(pkg.limit);
                var currentVersion = Xamarin.Essentials.AppInfo.Version;
                if (currentVersion < limitVersion)
                {
                    logger.Log($"need update version: {currentVersion}->{limitVersion}");
                    handler?.Invoke(LoadResult.NEED_UPDATE, pkg.url);
                    return;
                }

                //设置本次将连接的服务器
                var uri = selectUrl.Split(":");
                App.AssetServerURI = new Tuple<string, int>(uri[0], int.Parse(uri[1]));

                handler?.Invoke(LoadResult.OK, "");
            }
            catch (System.Exception e)
            {

                logger.Log("load config error", e);
                handler?.Invoke(LoadResult.NOT_AVAILABLE, "解析配置出错，可能是服务器有问题，请稍后重试");
            }
        }

        string TS(long t)
        {
            if (t == 0) return "";
            var date = (new DateTime(1970, 1, 1)).AddMilliseconds(t).ToLocalTime();
            return date.ToString();

        }

        async Task LoadInit()
        {
            //测试清除
            if (startOption.GetBoolValue("resetServerLoader")) File.Delete(SSFile);

            string ssServers = null;
            try
            {
                ssServers = File.ReadAllText(SSFile);
            }
            catch (Exception e)
            {
                logger.Log("load ssfile failed!", SSKeys, e);
            }

            //如果运行时配置还未生成，就从内置包里生成初值
            if (ssServers == null)
            {
                ssServers = mb.Util.ReadBuiltinFile("servers.json");
                logger.Log("first run, load from servers.json");
            }

            try
            {
                string etag = "mbec:";
                logger.Log("ssServers", ssServers);
                if (ssServers.IndexOf(etag) == 0)
                    ssServers = mb.Util.Decrypt(ssServers.Substring(etag.Length), passwd);

                serverConfig = JsonMapper.ToObject<ServerConfig>(ssServers);
            }
            catch (System.Exception e)
            {
                logger.Log("parse server failed", e);
                File.Delete(SSFile);
                serverConfig = new ServerConfig();
            }
        }

        public List<ServerItem> GetAvailabeServers(ServerType st)
        {
            var now = DateTimeOffset.Now.ToUnixTimeMilliseconds();
            var theServers = serverConfig.servers[(int) st];
#if DEBUG
            logger.Log("-----load servers", theServers.Count, TS(now));
            foreach (var si in theServers)
            {
                var ats = TS(si.availableTime);
                var lts = TS(si.lastConnectTime);
                var s = String.Format("{0}, last:{1}, avai:{2}", si.site.PadRight(30, '-'), lts.PadRight(20, '-'), ats.PadRight(20, '-'));
                logger.Log(s);
            }
            logger.Log("-----------------------------------\n");
#endif
            //-/////////////////////////

            var availableServer = theServers.ToList().Where(si => { return si.availableTime != -1 && si.availableTime <= now; }).ToList();
            availableServer.Sort((a, b) =>
            {
                if (a.lastConnectTime == b.lastConnectTime) return 0;
                if (a.lastConnectTime > b.lastConnectTime) return -1;
                return 1;
            });

            //-/////////////////////////
#if DEBUG
            logger.Log("-----sort servers", availableServer.Count);
            foreach (var si in availableServer)
            {
                var ats = TS(si.availableTime);
                var lts = TS(si.lastConnectTime);
                var s = String.Format("{0}, last:{1}, avai:{2}", si.site.PadRight(30, '-'), lts.PadRight(20, '-'), ats.PadRight(20, '-'));
                logger.Log(s);
            }
            logger.Log("-----------------------------------\n");
#endif
            //-/////////////////////////

            return availableServer;
        }

        void Merge(ServerConfig newConfig, string curUseSite)
        {
            for (int i = 0; i < serverConfig.servers.Length; i++)
            {
                var theServers = serverConfig.servers[i];
                var esi = theServers.Find(fsi => fsi.site == curUseSite);
                if (esi != null)
                {
                    esi.lastConnectTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                }

                //遍历新列表
                foreach (var si in newConfig.servers[i])
                {
                    //如果是删除型的，就把原列表里对应的删除
                    if (si.delete)
                    {
                        int n = theServers.RemoveAll(fsi => Regex.IsMatch(fsi.site, si.site));
                        logger.Log("removed servers", n, si.site);
                    }
                    else
                    {
                        esi = theServers.Find(fsi => fsi.site == si.site);
                        if (esi == null) //如果没找到，就做为新的添加
                        {
                            theServers.Add(si);
                        }
                        else //如果找到了，就更新启用时间 
                        {
                            esi.availableTime = si.availableTime;
                        }
                    }
                }
            }
        }
        void Save()
        {
            var str = JsonMapper.ToJson(serverConfig);
            File.WriteAllText(SSFile, "mbec:" + mb.Util.Encrypt(str, passwd));
            logger.Log("save servers", str);
        }

        byte[] passwd = System.Text.ASCIIEncoding.ASCII.GetBytes("xiaoting");
    }
}